﻿using System;
using Algoritmia.StateSpaces;
using Algoritmia.Utils;
using Lists = Algoritmia.DataStructures.Lists;
using System.Linq;

namespace Algoritmia.Schemes
{

    public class RecursiveGreedySolver<TState,TDecision,TSolution>
    {
        protected readonly Func<IForwardStateSpace<TState, TDecision>, Lists.IList<TDecision>, TState, TSolution> _createSolution;
        protected readonly Func<Lists.IList<TDecision>> _createList;
        protected Func<IForwardStateSpace<TState, TDecision>, TState, TDecision> _decisionSelector;

        public RecursiveGreedySolver(Func<IForwardStateSpace<TState,TDecision>,Lists.IList<TDecision>,TState,TSolution> createSolution,
                                     Func<Lists.IList<TDecision>> createList = null,
                                     Func<IForwardStateSpace<TState,TDecision>,TState,TDecision> decisionSelector = null
                                    )
        {
            _createSolution = createSolution;
            _createList = createList ?? (() => AlgoritmiaFactory.Lists<TDecision>.Default);
            _decisionSelector = decisionSelector ?? ((space, state) => (space.Decisions(state).First()));
        }

        public virtual TSolution Solve(IForwardStateSpace<TState,TDecision> stateSpace)
        {
            var decisions = _createList();
            Func<TState, TSolution> solveRecursively = null;
            solveRecursively = s =>
                                   {
                                       if (stateSpace.IsFinal(s))
                                       {
                                           return _createSolution(stateSpace, decisions, s);
                                       }
                                       var d = _decisionSelector(stateSpace, s);
                                       decisions.Add(d);
                                       return solveRecursively(stateSpace.Decide(s, d));
                                   };
            return solveRecursively(stateSpace.InitialStates.First());
        }
    }

    public class GreedySolver<TState,TDecision,TSolution> : RecursiveGreedySolver<TState,TDecision,TSolution>
    {
        public GreedySolver(Func<IForwardStateSpace<TState, TDecision>, Lists.IList<TDecision>, TState, TSolution> createSolution,
                             Func<Lists.IList<TDecision>> createList = null,
                             Func<IForwardStateSpace<TState, TDecision>, TState, TDecision> decisionSelector = null
                            )
            : base(createSolution,createList,decisionSelector)
        {
        }

        public override TSolution Solve(IForwardStateSpace<TState, TDecision> stateSpace)
        {
            var decisions = _createList();
            var initial = stateSpace.InitialStates.First();
            TState state = initial;
            while (!stateSpace.IsFinal(state))
            {
                var d = _decisionSelector(stateSpace, state);
                state = stateSpace.Decide(state, d);
                decisions.Add(d);
            }
            return _createSolution(stateSpace, decisions, state);
        }
    }
}
